﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using System.Data;
using DDTek.Oracle;

namespace Sofire.Data
{
    /// <summary>
    /// 基于 Oracle 查询与交互的操作引擎。
    /// </summary>
    public class OracleQuery : QueryEngineBase
    {
        /// <summary>
        /// 获取授权文件的名称。
        /// </summary>
        public const string DDTekLic = "DDTek.lic";
        private static string _LicensePath;
        /// <summary>
        /// 设置或获取 ORACLE 授权文件路径。
        /// </summary>
        public static string LicensePath
        {
            get
            {
                if(_LicensePath == null) _LicensePath = System.IO.Path.Combine(GA.AppDirectory, DDTekLic);
                return _LicensePath;
            }
            set
            {
                _LicensePath = value;
            }
        }

        #region Constructors

        private static string CheckConnectionString(string connectionString)
        {
            if(connectionString != null && connectionString.IndexOf("License Path=", StringComparison.CurrentCultureIgnoreCase) == -1)
            {
                if(!connectionString.EndsWith(";")) connectionString += ";";
                connectionString += "License Path=" + LicensePath;
            }
            return connectionString;
        }

        /// <summary>
        /// 初始化 <see cref="Sofire.Data.OracleQuery"/> 的新实例。
        /// </summary>
        /// <param name="connectionString">连接字符串。</param>
        public OracleQuery(string connectionString)
            : base(QueryEngineProvider.Oracle, CheckConnectionString(connectionString)) { }

        /// <summary>
        /// 指定主机、端口、服务器、用户名和密码，初始化 <see cref="Sofire.Data.OracleQuery"/> 的新实例。
        /// </summary>
        /// <param name="host">主机。</param>
        /// <param name="port">端口。</param>
        /// <param name="sid">服务器。</param>
        /// <param name="user">用户名。</param>
        /// <param name="password">密码。</param>
        public OracleQuery(string host, int port, string sid, string user, string password)
            : this(string.Format("HOST={0};PORT={1};SID={2};USER ID={3};PASSWORD={4};License Path={5}"
            , host, port, sid, user, password, _LicensePath)) { }

        /// <summary>
        /// 指定主机、服务器、用户名和密码，初始化 <see cref="Sofire.Data.OracleQuery"/> 的新实例。
        /// </summary>
        /// <param name="host">主机。</param>
        /// <param name="sid">服务器。</param>
        /// <param name="user">用户名。</param>
        /// <param name="pwd">密码。</param>
        public OracleQuery(string host, string sid, string user, string pwd)
            : this(host, 1521, sid, user, pwd) { }

        /// <summary>
        /// 指定服务器、用户名和密码，初始化 <see cref="Sofire.Data.OracleQuery"/> 的新实例。
        /// </summary>
        /// <param name="server">服务器。</param>
        /// <param name="user">用户名。</param>
        /// <param name="pwd">密码。</param>
        public OracleQuery(string server, string user, string pwd)
            : this("localhost", server, user, pwd) { }

        #endregion Constructors

        private bool _BindByName = true;
        /// <summary>
        /// 设置或获取一个值，指示是否绑定参数时，采用参数名绑定。
        /// <para>默认为 true，表示启用参数名绑定。</para>
        /// </summary>
        public bool BindByName
        {
            get
            {
                return this._BindByName;
            }
            set
            {
                this._BindByName = value;
            }
        }

        #region Methods

        /// <summary>
        /// 创建并返回一个 <see cref="DDTek.Oracle.OracleCommandBuilder"/> 查询命令生成器。
        /// </summary>
        /// <returns>返回一个查询命令生成器。</returns>
        public override DbCommandBuilder CreateCommandBuilder()
        {
            return new OracleCommandBuilder();
        }

        /// <summary>
        /// 创建并返回一个 <see cref="DDTek.Oracle.OracleConnection"/> 数据源。
        /// </summary>
        /// <returns>返回一个数据库的连接。</returns>
        public override DbConnection CreateConnection()
        {
            return this.CreateOracleConnection();
        }

        /// <summary>
        /// 创建并返回一个 <see cref="DDTek.Oracle.OracleConnection"/> 数据源。
        /// </summary>
        /// <returns>返回一个数据库的连接。</returns>
        protected virtual OracleConnection CreateOracleConnection()
        {
            return new OracleConnection(this.ConnectionString);
        }

        /// <summary>
        /// 创建并返回一个 <see cref="DDTek.Oracle.OracleDataAdapter"/> 数据适配器。
        /// </summary>
        /// <param name="command">命令对象。</param>
        /// <returns>返回一个数据适配器。</returns>
        public override DbDataAdapter CreateDataAdapter(DbCommand command)
        {
            return new OracleDataAdapter(command as OracleCommand);
        }

        /// <summary>
        /// 创建并返回一个批量加载的实现。
        /// </summary>
        /// <returns>返回一个基于当前连接的实例。</returns>
        public OracleBulkCopy CreateBulkCopy()
        {
            return new OracleBulkCopy(this.CreateOracleConnection());
        }

        /// <summary>
        /// 实例化一个指定数据源的命令。
        /// </summary>
        /// <param name="conn">指定数据源。</param>
        /// <returns>返回一个新创建的命令。</returns>
        protected override DbCommand NewCommand(DbConnection conn)
        {

            OracleCommand command = base.NewCommand(conn) as OracleCommand;
            //INSERT INTO t_demo VALUES (?,?,?);
            command.BindByName = this._BindByName;
            //启用 RefCursor 模式
            command.UseExplicitRefCursorMode = true;
            return command;
        }

        /// <summary>
        /// 创建并返回一个与给定数据连接关联的命令对象。
        /// </summary>
        /// <param name="conn">指定数据源。</param>
        /// <param name="commandText">Transact-SQL 语句。第一个字符为“>”时，表示一个存储过程。</param>
        /// <returns>返回一个命令对象。</returns>
        protected override DbCommand CreateCommand(DbConnection conn, string commandText)
        {
            if(string.IsNullOrEmpty(commandText)) throw new ArgumentNullException("commandText");
            return base.CreateCommand(conn, commandText
                .Replace(Convert.ToChar(9), ' ')
                .Replace(Convert.ToChar(10), ' ')
                .Replace(Convert.ToChar(13), ' '));
        }

        #endregion Methods

        /// <summary>
        /// 获取参数名称的前缀（T-SQL 文本）。
        /// </summary>
        public override string ParameterPrefixWithText
        {
            get
            {
                return ":";
            }
        }
        /// <summary>
        /// 获取参数名称的前缀（参数集合）。
        /// </summary>
        public override string ParameterPrefixWithCollection
        {
            get
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 指定游标的名称，创建一个 <see cref="ParameterDirection.Output"/> 的 Oracle 数据库的游标。
        /// </summary>
        /// <param name="name">游标的名称。</param>
        /// <returns>返回一个 <see cref="Sofire.Data.ExecuteParameter"/>。</returns>
        public ExecuteParameter CreateCursor(string name)
        {
            return new ExecuteParameter(new OracleParameter(name, OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
        }

        /// <summary>
        /// 描述指定的 <see cref="System.Data.Common.DbParameter"/>。
        /// </summary>
        /// <param name="parameter">一个 <see cref="System.Data.Common.DbParameter"/> 的实例。</param>
        /// <returns>返回参数描述的 <see cref="System.String"/> 值。</returns>
        public override string DescribeParameter(DbParameter parameter)
        {
            var p = parameter as OracleParameter;
            string desc = p.ParameterName;

            switch(p.Direction)
            {
                case ParameterDirection.InputOutput:
                    desc += " IN OUT";
                    break;
                case ParameterDirection.Output:
                    desc += " OUT";
                    break;
            }
            desc += " " + p.OracleDbType;
            switch(p.OracleDbType)
            {
                case OracleDbType.Char:
                case OracleDbType.NChar:
                case OracleDbType.NVarChar:
                case OracleDbType.Number:
                case OracleDbType.Double:
                case OracleDbType.Float:
                case OracleDbType.VarChar:
                    desc += "(" + p.Size + ")";
                    break;
            }
            return desc;
        }
    }
}
